package priv.amos.androidnetwork.connector;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import cz.msebera.android.httpclient.HttpEntity;
import cz.msebera.android.httpclient.HttpResponse;
import cz.msebera.android.httpclient.HttpVersion;
import cz.msebera.android.httpclient.NameValuePair;
import cz.msebera.android.httpclient.client.CookieStore;
import cz.msebera.android.httpclient.client.entity.UrlEncodedFormEntity;
import cz.msebera.android.httpclient.client.methods.HttpGet;
import cz.msebera.android.httpclient.client.methods.HttpPost;
import cz.msebera.android.httpclient.conn.ClientConnectionManager;
import cz.msebera.android.httpclient.conn.params.ConnManagerParams;
import cz.msebera.android.httpclient.conn.scheme.PlainSocketFactory;
import cz.msebera.android.httpclient.conn.scheme.Scheme;
import cz.msebera.android.httpclient.conn.scheme.SchemeRegistry;
import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory;
import cz.msebera.android.httpclient.cookie.Cookie;
import cz.msebera.android.httpclient.impl.client.DefaultHttpClient;
import cz.msebera.android.httpclient.impl.conn.tsccm.ThreadSafeClientConnManager;
import cz.msebera.android.httpclient.params.BasicHttpParams;
import cz.msebera.android.httpclient.params.HttpConnectionParams;
import cz.msebera.android.httpclient.params.HttpParams;
import cz.msebera.android.httpclient.params.HttpProtocolParams;
import cz.msebera.android.httpclient.protocol.HTTP;
import priv.amos.androidnetwork.bean.AndroidConnection;
import priv.amos.androidnetwork.bean.ResponseData;
import priv.amos.androidnetwork.type.ConnMethodType;
import priv.amos.androidnetwork.type.DataType;
import priv.amos.queue.entity.Message;
import priv.amos.queue.entity.Queue;
import priv.amos.queue.listener.MessageListener;

public class ConnectionProcessor {
	private Queue queue;
	private DefaultHttpClient client;
	private PersistentCookieStore pcs;
	public ConnectionProcessor(int connThreadsSize,int connTimeout,int soTimeout){
		queue = new Queue(new ConnectionListener(),connThreadsSize);
		HttpParams params = new BasicHttpParams();
		HttpConnectionParams.setConnectionTimeout(params, connTimeout);
		HttpConnectionParams.setSoTimeout(params, soTimeout);
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);  
        HttpProtocolParams.setUseExpectContinue(params, true);  
        SchemeRegistry schReg = new SchemeRegistry();  
        schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));  
        schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
        ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);  
        client = new DefaultHttpClient(conMgr, params); 
	}
	public synchronized void newConnection(AndroidConnection conn) {
		Message msg = new Message();
		msg.obj = conn;
		queue.addMessage(msg);
	}
	private class ConnectionListener implements MessageListener {

		@Override
		public void processMessage(Message msg) {
			AndroidConnection conn = (AndroidConnection) msg.obj;
			String connType = conn.getConnectionType();
			String dataType=conn.getDataType();
			String url = conn.getUrl();
			Context ctx = conn.getCtx();
			Handler h = conn.getH();
			List<NameValuePair> nvps = conn.getNvps();
			if(connType.equals(ConnMethodType.GET)){
				ResponseData data=getRequest(ctx,url,dataType);
				android.os.Message mes=new android.os.Message();
				mes.obj=data;
				h.sendMessage(mes);
			}
			else{
				ResponseData data=postRequest(ctx,url,dataType,nvps);
				android.os.Message mes=new android.os.Message();
				mes.obj=data;
				h.sendMessage(mes);
			}
		}
	}
	private ResponseData getRequest(Context ctx,String url,String dataType){

		//synchronized (o) {
			HttpGet getMethod = new HttpGet(url);
			HttpResponse response = null;
			String content = null;
			ResponseData res=new ResponseData();
			res.dataType=dataType;
			res.connType=ConnMethodType.GET;
			res.url=url;
			try {
				response = client.execute(getMethod);
				refreshCookie(ctx);
				int code=response.getStatusLine().getStatusCode();
				res.statusCode=code;
				
				switch(dataType){
					case DataType.STRING:
						content = inputStreamToString(response.getEntity().getContent());
						res.data=content;
						break;
					case DataType.IMAGE:
						HttpEntity entity = response.getEntity();
						InputStream is;
						is = entity.getContent();
						Bitmap pic = BitmapFactory
								.decodeStream(new PatchInputStream(is));
						res.data = pic;
						break;
				}
			}  catch (Exception e) {
				res.exception=e;
			} finally {
				if (response != null) {
					if (response != null) {
						HttpEntity entity = response.getEntity();
						if (entity == null) {

						} else if (entity.isStreaming()) {
							InputStream instream;
							try {
								instream = entity.getContent();
								if (instream != null) {
									instream.close();
								}
							} catch (Exception e) {
								res.exception=e;
							}

						}
					}
				}
				getMethod.releaseConnection();
			}
			return res;
	}
	private ResponseData postRequest(Context ctx,String url,String dataType,List<NameValuePair> nvps){
			ResponseData res=new ResponseData();
			HttpPost postMethod = new HttpPost(url);
			res.dataType=dataType;
			res.connType=ConnMethodType.POST;
			res.url=url;
			if(nvps!=null){
				try {
					postMethod
							.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
				} catch (UnsupportedEncodingException e1) {
					e1.printStackTrace();
				}
			}
			HttpResponse response = null;
			
			try {
				response = client.execute(postMethod);
				refreshCookie(ctx);
				int code=response.getStatusLine().getStatusCode();
				res.statusCode=code;
				switch(dataType){
					case DataType.STRING:
						String content = inputStreamToString(response.getEntity().getContent());
						res.data=content;
						break;
				}
			} catch (Exception e) {
				res.exception=e;
			} finally {
				if (response != null) {
					// EntityUtils.consumeQuietly(response.getEntity());
					HttpEntity entity = response.getEntity();
					if (entity == null) {

					} else if (entity.isStreaming()) {
						InputStream instream;
						try {
							instream = entity.getContent();
							if (instream != null) {
								instream.close();
							}
						} catch (Exception e) {
							res.exception=e;
						}

					}
				}
				postMethod.releaseConnection();
			}
			return res;
	}
	public synchronized  void refreshCookie(Context ctx) {
		if (pcs == null) {
			init(ctx);
		}
		pcs.clear();
		List<Cookie> cookies = client.getCookieStore().getCookies();
		for (Cookie cookie : cookies) {
			pcs.addCookie(cookie);
		}
	}

	public synchronized  void endCookies(Context ctx) {
		if (pcs == null) {
			init(ctx);
		}
		pcs.expire();
	}

	public synchronized  void startCookies(Context ctx) {
		if (pcs == null) {
			init(ctx);
		}
		pcs.start();
	}

	public synchronized  boolean isCookieExpired(Context ctx,long timeout) {
		if (pcs == null) {
			init(ctx);
		}
		if (!pcs.getIsConnected()) {
			return true;
		}
		long nowtime = (new Date()).getTime();
		if (nowtime < (timeout + pcs.getLastUpdate())) {
			return false;
		}
		return true;
	}

	public synchronized  void init(Context ctx) {
		pcs = new PersistentCookieStore(ctx);
		List<Cookie> cookies = pcs.getCookies();
		CookieStore cs = client.getCookieStore();
		for (Cookie cookie : cookies) {
			cs.addCookie(cookie);
		}
		client.setCookieStore(cs);
		// ToastUtils.showToast(ctx,
		// "cookies length is "+cs.getCookies().size());
	}
	private static String inputStreamToString(InputStream is) {
		String line = "";
		StringBuilder total = new StringBuilder();

		// Wrap a BufferedReader around the InputStream
		BufferedReader rd = new BufferedReader(new InputStreamReader(is));

		// Read response until the end
		try {
			while ((line = rd.readLine()) != null) {
				total.append(line);
			}
		} catch (IOException e) {
		}
		return total.toString();
	}
}
